#include "UtilityCard.h"

bool UtilityCard::init(uint8_t sckRateID, uint8_t chipSelectPin){
	Serial.println("utility init");
	uint8_t retVal;
	//_chipSelect();
	digitalWrite(10,LOW);

	_cTransfer(CMD_ESC);
	_cTransfer(CMD_SDINIT);
	_cTransfer (sckRateID);

	_cTransfer (chipSelectPin);

	while(digitalRead(MISO)) {
	//Serial.println("wait");
	}

	retVal = _cTransfer(CMD_ENDINTURRPTCOMMAND);

	//_chipDeselect();
	digitalWrite(10,HIGH);
	return(retVal);

}

uint32_t UtilityCard::cardSize(){
	uint32_t retVal;
	uint8_t nibble;
	//_chipSelect();
	digitalWrite(10,LOW);

	_cTransfer(CMD_ESC);
	_cTransfer(CMD_SDSIZE);

	while(digitalRead(MISO)) {};// hold while controller is busy

	_cTransfer(CMD_GETDATA);//resume

	nibble = _cTransfer(CMD_GETDATA);
	retVal = nibble;

	nibble = _cTransfer(CMD_GETDATA);

	retVal = (retVal<<8) | nibble;

	nibble = _cTransfer(CMD_GETDATA);
	retVal = (retVal<<8) | nibble;

	nibble = _cTransfer(CMD_GETDATA);
	retVal = (retVal<<8) | nibble;

	_cTransfer(CMD_ENDINTURRPTCOMMAND);

	//_chipDeselect();
	digitalWrite(10,HIGH);
	return(retVal);

}

bool UtilityCard::readBlock(uint32_t blockNumber, uint8_t* dst) {
	bool retVal;
	uint8_t nibble;
	//_chipSelect();
	digitalWrite(10,LOW);

	_cTransfer(CMD_ESC);

	_cTransfer(CMD_READBLOCK);
	_cTransfer(blockNumber>>24);
	_cTransfer(blockNumber>>16);
	_cTransfer(blockNumber>>8);
	_cTransfer(blockNumber);

	while(digitalRead(MISO)) {};// hold while controller is busy

	_cTransfer(CMD_GETDATA);//resume

	retVal = _cTransfer(CMD_GETDATA);
	if(retVal){
		for (int i = 0; i < 512; ++i) {
			dst[i] = _cTransfer(CMD_GETDATA);
		}
	}

	_cTransfer(CMD_ENDINTURRPTCOMMAND);

	//_chipDeselect();
	digitalWrite(10,HIGH);
	return(retVal);
}

bool UtilityCard::readCID(cid_t* cid) {
	bool retVal;
	uint8_t nibble;
	//_chipSelect();
	digitalWrite(10,LOW);

	_cTransfer(CMD_ESC);

	_cTransfer(CMD_READCID);

	while(digitalRead(MISO)) {};// hold while controller is busy

	_cTransfer(CMD_GETDATA);//resume

	retVal = _cTransfer(CMD_GETDATA); //get return value
	if(retVal){ // if success

		cid->mid= _cTransfer(CMD_GETDATA);
		cid->oid[0]= _cTransfer(CMD_GETDATA);
		cid->oid[1]= _cTransfer(CMD_GETDATA);
		cid->pnm[0]= _cTransfer(CMD_GETDATA);
		cid->pnm[1]= _cTransfer(CMD_GETDATA);
		cid->pnm[2]= _cTransfer(CMD_GETDATA);
		cid->pnm[3]= _cTransfer(CMD_GETDATA);
		cid->pnm[4]= _cTransfer(CMD_GETDATA);
		cid->prv_m= _cTransfer(CMD_GETDATA);
		cid->prv_n= _cTransfer(CMD_GETDATA);

		nibble = _cTransfer(CMD_GETDATA);
		cid->psn = nibble;

		nibble = _cTransfer(CMD_GETDATA);
		cid->psn = (cid->psn<<8) | nibble;

		nibble = _cTransfer(CMD_GETDATA);
		cid->psn = (cid->psn<<8) | nibble;

		nibble = _cTransfer(CMD_GETDATA);
		cid->psn = (cid->psn<<8) | nibble;

		cid->mdt_year_high= _cTransfer(CMD_GETDATA);
		cid->reserved= _cTransfer(CMD_GETDATA);
		cid->mdt_month= _cTransfer(CMD_GETDATA);
		cid->mdt_year_low= _cTransfer(CMD_GETDATA);
		cid->always1= _cTransfer(CMD_GETDATA);
		cid->crc= _cTransfer(CMD_GETDATA);

	}

	_cTransfer(CMD_ENDINTURRPTCOMMAND);

	//_chipDeselect();
	digitalWrite(10,HIGH);
	return(retVal);
}

int UtilityCard::type(){
	uint8_t retVal;
	//_chipSelect();
	digitalWrite(10,LOW);

	_cTransfer(CMD_ESC);
	_cTransfer(CMD_SDTYPE);

	while(digitalRead(MISO)) {};// hold while controller is busy

	retVal = _cTransfer(CMD_GETDATA);

	_cTransfer(CMD_ENDINTURRPTCOMMAND);

	//_chipDeselect();
	digitalWrite(10,HIGH);
	return(retVal);

}

bool UtilityCard::readStart(uint32_t blockNumber) {
	bool retVal;
	//_chipSelect();
	digitalWrite(10,LOW);

	_cTransfer(CMD_ESC);

	_cTransfer(CMD_READSTART);
	_cTransfer(blockNumber>>24);
	_cTransfer(blockNumber>>16);
	_cTransfer(blockNumber>>8);
	_cTransfer(blockNumber);

	while(digitalRead(MISO)) {};// hold while controller is busy

	retVal = _cTransfer(CMD_GETDATA);//resume

	_cTransfer(CMD_ENDINTURRPTCOMMAND);

	//_chipDeselect();
	digitalWrite(10,HIGH);
	return(retVal);
}

bool UtilityCard::readStop() {
	bool retVal;
	//_chipSelect();
	digitalWrite(10,LOW);

	_cTransfer(CMD_ESC);

	_cTransfer(CMD_READSTOP);
	while(digitalRead(MISO)) {};// hold while controller is busy

	retVal = _cTransfer(CMD_GETDATA);

	_cTransfer(CMD_ENDINTURRPTCOMMAND);

	//_chipDeselect();
	digitalWrite(10,HIGH);
	return(retVal);
}

bool UtilityCard::readData(uint8_t* dst) {
	bool retVal;
	//_chipSelect();
	digitalWrite(10,LOW);

	_cTransfer(CMD_ESC);

	_cTransfer(CMD_READDATA);

	while(digitalRead(MISO)) {};// hold while controller is busy

	retVal = _cTransfer(CMD_GETDATA);

	if(retVal){
		for (int i = 0; i < 512; ++i) {
			dst[i] = _cTransfer(CMD_GETDATA);
		}
	}

	_cTransfer(CMD_ENDINTURRPTCOMMAND);

	//_chipDeselect();
	digitalWrite(10,HIGH);
	return(retVal);
}

bool UtilityCard::writeBlock(uint32_t blockNumber, const uint8_t* src) {
	bool retVal;
	uint8_t nibble;
	//_chipSelect();
	digitalWrite(10,LOW);

	_cTransfer(CMD_ESC);

	_cTransfer(CMD_WRITEBLOCK);
	_cTransfer(blockNumber>>24);
	_cTransfer(blockNumber>>16);
	_cTransfer(blockNumber>>8);
	_cTransfer(blockNumber);

	for (int i = 0; i < 512; ++i) {
		_cTransfer(src[i]);
	}

	while(digitalRead(MISO)) {};// hold while controller is busy

	retVal = _cTransfer(CMD_GETDATA);

	_cTransfer(CMD_ENDINTURRPTCOMMAND);

	//_chipDeselect();
	digitalWrite(10,HIGH);
	return(retVal);
}

bool UtilityCard::writeStart(uint32_t blockNumber, uint32_t eraseCount) {
	bool retVal;
	//_chipSelect();
	digitalWrite(10,LOW);

	_cTransfer(CMD_ESC);

	_cTransfer(CMD_WRITESTART);
	_cTransfer(blockNumber>>24);
	_cTransfer(blockNumber>>16);
	_cTransfer(blockNumber>>8);
	_cTransfer(blockNumber);

	_cTransfer(eraseCount>>24);
	_cTransfer(eraseCount>>16);
	_cTransfer(eraseCount>>8);
	_cTransfer(eraseCount);

	while(digitalRead(MISO)) {};// hold while controller is busy

	retVal = _cTransfer(CMD_GETDATA);

	_cTransfer(CMD_ENDINTURRPTCOMMAND);

	//_chipDeselect();
	digitalWrite(10,HIGH);
	return(retVal);
}

bool UtilityCard::writeStop() {
	bool retVal;
	//_chipSelect();
	digitalWrite(10,LOW);

	_cTransfer(CMD_ESC);

	_cTransfer(CMD_WRITESTOP);
	while(digitalRead(MISO)) {};// hold while controller is busy

	retVal = _cTransfer(CMD_GETDATA);

	_cTransfer(CMD_ENDINTURRPTCOMMAND);

	//_chipDeselect();
	digitalWrite(10,HIGH);
	return(retVal);
}

bool UtilityCard::writeData(const uint8_t* src) {
	bool retVal;
	//_chipSelect();
	digitalWrite(10,LOW);

	_cTransfer(CMD_ESC);

	_cTransfer(CMD_WRITEDATA);

	for (int i = 0; i < 512; ++i) {
		_cTransfer(src[i]);
	}


	while(digitalRead(MISO)) {};// hold while controller is busy

	retVal = _cTransfer(CMD_GETDATA);

	_cTransfer(CMD_ENDINTURRPTCOMMAND);

	//_chipDeselect();
	digitalWrite(10,HIGH);
	return(retVal);
}

bool UtilityCard::setSckRate(uint8_t sckRateID) {
	bool retVal;
	//_chipSelect();
	digitalWrite(10,LOW);

	_cTransfer(CMD_ESC);

	_cTransfer(CMD_SETSCKRATE);
	_cTransfer(sckRateID);

	while(digitalRead(MISO)) {};// hold while controller is busy

	retVal = _cTransfer(CMD_GETDATA);

	_cTransfer(CMD_ENDINTURRPTCOMMAND);

	//_chipDeselect();
	digitalWrite(10,HIGH);
	return(retVal);
}

bool UtilityCard::erase(uint32_t startBlock, uint32_t endBlock) {
	bool retVal;
	//_chipSelect();
	digitalWrite(10,LOW);

	_cTransfer(CMD_ESC);

	_cTransfer(CMD_ERASE);
	_cTransfer(startBlock>>24);
	_cTransfer(startBlock>>16);
	_cTransfer(startBlock>>8);
	_cTransfer(startBlock);

	_cTransfer(endBlock>>24);
	_cTransfer(endBlock>>16);
	_cTransfer(endBlock>>8);
	_cTransfer(endBlock);

	while(digitalRead(MISO)) {};// hold while controller is busy

	retVal = _cTransfer(CMD_GETDATA);

	_cTransfer(CMD_ENDINTURRPTCOMMAND);

	//_chipDeselect();
	digitalWrite(10,HIGH);
	return(retVal);
}


bool UtilityCard::eraseSingleBlockEnable() {
	bool retVal;
	//_chipSelect();
	digitalWrite(10,LOW);

	_cTransfer(CMD_ESC);

	_cTransfer(CMD_ERASESINGLEBLOCKENABLE);

	while(digitalRead(MISO)) {};// hold while controller is busy

	retVal = _cTransfer(CMD_GETDATA);

	_cTransfer(CMD_ENDINTURRPTCOMMAND);

	//_chipDeselect();
	digitalWrite(10,HIGH);
	return(retVal);
}

void UtilityCard::error(uint8_t code) {
	//_chipSelect();
	digitalWrite(10,LOW);

	_cTransfer(CMD_ESC);

	_cTransfer(CMD_SDERROR);
	_cTransfer(code);

	_cTransfer(CMD_ENDINTURRPTCOMMAND);

	//_chipDeselect();
	digitalWrite(10,HIGH);
}

int UtilityCard::errorCode() {
	uint8_t retVal;
	//_chipSelect();
	digitalWrite(10,LOW);

	_cTransfer(CMD_ESC);
	_cTransfer(CMD_SDERRORCODE);
	retVal = _cTransfer(CMD_GETDATA);

	_cTransfer(CMD_ENDINTURRPTCOMMAND);

	//_chipDeselect();
	digitalWrite(10,HIGH);
	return(retVal);
}

int UtilityCard::errorData() {
	uint8_t retVal;
	//_chipSelect();
	digitalWrite(10,LOW);

	_cTransfer(CMD_ESC);
	_cTransfer(CMD_SDERRORDATA);
	retVal = _cTransfer(CMD_GETDATA);

	_cTransfer(CMD_ENDINTURRPTCOMMAND);

	//_chipDeselect();
	digitalWrite(10,HIGH);
	return(retVal);
}

bool UtilityCard::_transfer(uint8_t byte)
{
    _cTransfer(byte);
	if(byte==0x00) { // if it's a null byte escape it
		_cTransfer(byte);
	}
	return(true);
}

byte UtilityCard::_cTransfer(uint8_t byte) // command transfer, without auto null escaping
{
	static uint8_t lastTransmit = 0;
	uint8_t returnValue = 0;
    returnValue = SPI.transfer (byte);
	lastTransmit = byte;
	delayMicroseconds (SC_BYTEDELAY);

	return(returnValue);
}
